package libra;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPathPredicateItem;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPredicateItem;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.GatewayApiDefinitionManager;
import com.alibaba.csp.sentinel.adapter.gateway.common.command.UpdateGatewayApiDefinitionGroupCommandHandler;
import com.alibaba.csp.sentinel.adapter.gateway.common.command.UpdateGatewayRuleCommandHandler;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.csp.sentinel.datasource.FileRefreshableDataSource;
import com.alibaba.csp.sentinel.datasource.FileWritableDataSource;
import com.alibaba.csp.sentinel.datasource.ReadableDataSource;
import com.alibaba.csp.sentinel.datasource.WritableDataSource;
import com.alibaba.csp.sentinel.init.InitFunc;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRuleManager;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import com.alibaba.csp.sentinel.slots.system.SystemRule;
import com.alibaba.csp.sentinel.slots.system.SystemRuleManager;
import com.alibaba.csp.sentinel.transport.util.WritableDataSourceRegistry;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;

public class InitRules implements InitFunc{
	
	String ruleDir = "C:/Users/libra/OneDrive/UserSysConfig/ForTest";
	String gatewayRulePath = ruleDir + "/sentiel-gateway-rule.json";
	String gatewayApiGroupPath = ruleDir + "/sentiel-gateway-api-group.json";
	String gatewayDegradeRulePath = ruleDir + "/sentiel-gateway-degrade-rule.json";
	String gatewaySystemRulePath = ruleDir + "/sentiel-gateway-system-rule.json";
	String gatewayAuthorityRulePath = ruleDir + "/sentiel-gateway-authority-rule.json";
	
//	public static void main(String[] args) {
//		String json = "[{\"apiName\":\"API分组测试\",\"predicateItems\":[{\"matchStrategy\":0,\"pattern\":\"/group/apiGroup/precise\"}]}]";
//		System.out.println( JSONObject.parseArray(json, ApiDefinition.class) );
//	}
	
    public void init() throws Exception {
    	/*************************************************
         * Gateway集成Sentinel，自定义动态加载、持久化配置 *
         *************************************************/
        // Gateway流控规则
        ReadableDataSource<String, Set<GatewayFlowRule>> flowRuleRDS = 
    		new FileRefreshableDataSource<>( gatewayRulePath, new ReadGatewayConverter<GatewayFlowRule>(GatewayFlowRule.class) );
	    // 将可读数据源注册至GatewayRuleManager
	    // 这样当规则文件发生变化时，就会更新规则到内存
        GatewayRuleManager.register2Property(flowRuleRDS.getProperty());
	    
	    WritableDataSource<Set<GatewayFlowRule>> flowRuleWDS = 
	    	new FileWritableDataSource<>( gatewayRulePath, new WriteGatewayConverter<GatewayFlowRule>() );
	    // 将可写数据源通过sentinel-api-gateway-adapter-common.jar的UpdateGatewayRuleCommandHandler进行注册
	    // 这样收到控制台推送的规则时，Sentinel会先更新到内存，然后将规则写入到文件中
	    UpdateGatewayRuleCommandHandler.setWritableDataSource(flowRuleWDS);
	    
	    
        //GatewayAPI分组
        ReadableDataSource<String, Set<ApiDefinition>> apiGroupRDS = 
    		new FileRefreshableDataSource<>( gatewayApiGroupPath, new ReadGatewayConverter<ApiDefinition>(ApiDefinition.class) );
	    // 将可读数据源注册至GatewayApiDefinitionManager
	    // 这样当规则文件发生变化时，就会更新规则到内存
        GatewayApiDefinitionManager.register2Property(apiGroupRDS.getProperty());
        
	    WritableDataSource<Set<ApiDefinition>> apiGroupWDS = 
			new FileWritableDataSource<>( gatewayApiGroupPath, new WriteGatewayConverter<ApiDefinition>() );
	    // 将可写数据源通过sentinel-api-gateway-adapter-common.jar的UpdateGatewayApiDefinitionGroupCommandHandler进行注册
	    // 这样收到控制台推送的规则时，Sentinel会先更新到内存，然后将规则写入到文件中
        UpdateGatewayApiDefinitionGroupCommandHandler.setWritableDataSource(apiGroupWDS);
        
        
    	/***********************************************
         * Gateway集成Sentinel，通用动态加载、持久化配置 *
         ***********************************************/
        //降级规则
        ReadableDataSource<String, List<DegradeRule>> degradeRuleRDS = 
        	new FileRefreshableDataSource<>( gatewayDegradeRulePath, new ReadConverter<DegradeRule>(DegradeRule.class) );
        DegradeRuleManager.register2Property(degradeRuleRDS.getProperty());
        
        WritableDataSource<List<DegradeRule>> degradeRuleWDS = 
        	new FileWritableDataSource<>( gatewayDegradeRulePath, new WriteConverter<DegradeRule>() );
        WritableDataSourceRegistry.registerDegradeDataSource(degradeRuleWDS);
        
        
        //系统规则
        ReadableDataSource<String, List<SystemRule>> systemRuleRDS = 
        	new FileRefreshableDataSource<>( gatewaySystemRulePath, new ReadConverter<SystemRule>(SystemRule.class) );
        SystemRuleManager.register2Property(systemRuleRDS.getProperty());
        
        WritableDataSource<List<SystemRule>> systemRuleWDS = 
        	new FileWritableDataSource<>( gatewaySystemRulePath, new WriteConverter<SystemRule>() );
        WritableDataSourceRegistry.registerSystemDataSource(systemRuleWDS);

        
        //授权规则
        ReadableDataSource<String, List<AuthorityRule>> authorityRuleRDS = 
        	new FileRefreshableDataSource<>( gatewayAuthorityRulePath, new ReadConverter<AuthorityRule>(AuthorityRule.class) );
        AuthorityRuleManager.register2Property(authorityRuleRDS.getProperty());
        
        WritableDataSource<List<AuthorityRule>> authorityRuleWDS = 
        	new FileWritableDataSource<>( gatewayAuthorityRulePath, new WriteConverter<AuthorityRule>() );
        WritableDataSourceRegistry.registerAuthorityDataSource(authorityRuleWDS);
    }
}
class ReadGatewayConverter <T> implements Converter<String, Set<T>> {
	private Class<T> clazzT;
	public ReadGatewayConverter(Class<T> clazzT) {
		super();
		this.clazzT = clazzT;
	}
	@Override
	public Set<T> convert(String source) {
		try {
			System.out.println("读取：" + source);
			return clazzT.equals(ApiDefinition.class) ? 
					parseJson(source) : Set.copyOf( JSONObject.parseArray(source, clazzT) );
		} catch (Exception e) {
			e.printStackTrace();
		}
		return Set.of();
	}
	
	// copy from com.alibaba.csp.sentinel.adapter.gateway.common.command.UpdateGatewayApiDefinitionGroupCommandHandler.parseJson(String data)
    private Set<T> parseJson(String data) {
        Set<T> apiDefinitions = new HashSet<>();
        JSONArray array = JSON.parseArray(data);
        for (Object obj : array) {
            JSONObject o = (JSONObject)obj;
            ApiDefinition apiDefinition = new ApiDefinition((o.getString("apiName")));
            Set<ApiPredicateItem> predicateItems = new HashSet<>();
            JSONArray itemArray = o.getJSONArray("predicateItems");
            if (itemArray != null) {
                predicateItems.addAll(itemArray.toJavaList(ApiPathPredicateItem.class));
            }
            apiDefinition.setPredicateItems(predicateItems);
            apiDefinitions.add((T) apiDefinition);
        }
        return apiDefinitions;
    }
}
class WriteGatewayConverter <T> implements Converter<Set<T>, String> {
	@Override
	public String convert(Set<T> source) {
		System.out.println("写入：" + source);
		return JSONObject.toJSONString(source);
	}
}

class ReadConverter <T> implements Converter<String, List<T>> {
	private Class<T> clazzT;
	public ReadConverter(Class<T> clazzT) {
		super();
		this.clazzT = clazzT;
	}
	@Override
	public List<T> convert(String source) {
		try {
			System.out.println("读取：" + source);
			return JSONObject.parseArray(source, clazzT);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return List.of();
	}
}
class WriteConverter <T> implements Converter<List<T>, String> {
	@Override
	public String convert(List<T> source) {
		System.out.println("写入：" + source);
		return JSONObject.toJSONString(source);
	}
}
